package com.yu.gulimall.product.service.impl;

import com.yu.gulimall.product.dao.CategoryBrandRelationDao;
import com.yu.gulimall.product.service.CategoryBrandRelationService;
import com.yu.gulimall.product.vo.Catelog2Vo;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yu.common.utils.PageUtils;
import com.yu.common.utils.Query;

import com.yu.gulimall.product.dao.CategoryDao;
import com.yu.gulimall.product.entity.CategoryEntity;
import com.yu.gulimall.product.service.CategoryService;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;


@Service("categoryService")
public class CategoryServiceImpl extends ServiceImpl<CategoryDao, CategoryEntity> implements CategoryService {

    /**
     * 每一个需要缓存的数据我们都来指定要放到那个名字的缓存。【缓存的分区(按照业务类型分)】
     * 代表当前方法的结果需要缓存，如果缓存中有，方法都不用调用，如果缓存中没有，会调用方法。最后将方法的结果放入缓存
     * 默认行为
     *      如果缓存中有，方法不再调用
     *      key是默认生成的:缓存的名字::SimpleKey::[](自动生成key值)
     *      缓存的value值，默认使用jdk序列化机制，将序列化的数据存到redis中
     *      默认时间是 -1：
     *
     *   自定义操作：key的生成
     *      指定生成缓存的key：key属性指定，接收一个Spel
     *      指定缓存的数据的存活时间:配置文档中修改存活时间
     *      将数据保存为json格式
     *
     *
     * 4、Spring-Cache的不足之处：
     *  1）、读模式
     *      缓存穿透：查询一个null数据。解决方案：缓存空数据
     *      缓存击穿：大量并发进来同时查询一个正好过期的数据。解决方案：加锁 ? 默认是无加锁的;使用sync = true来解决击穿问题
     *      缓存雪崩：大量的key同时过期。解决：加随机时间。加上过期时间
     *  2)、写模式：（缓存与数据库一致）
     *      1）、读写加锁。
     *      2）、引入Canal,感知到MySQL的更新去更新Redis
     *      3）、读多写多，直接去数据库查询就行
     *
     *  总结：
     *      常规数据（读多写少，即时性，一致性要求不高的数据，完全可以使用Spring-Cache）：写模式(只要缓存的数据有过期时间就足够了)
     *      特殊数据：特殊设计
     *
     *  原理：
     *      CacheManager(RedisCacheManager)->Cache(RedisCache)->Cache负责缓存的读写
     * @return
     */



    @Resource
    private CategoryBrandRelationDao categoryBrandRelationDao;

    @Override
    public PageUtils queryPage(Map<String, Object> params) {
        IPage<CategoryEntity> page = this.page(
                new Query<CategoryEntity>().getPage(params),
                new QueryWrapper<CategoryEntity>()
        );

        return new PageUtils(page);
    }

    @Override
    public List<CategoryEntity> listWithTree() {
        //查找所有分类
        List<CategoryEntity> categoryEntities = baseMapper.selectList(null);

        //查找一级分类 标志L:parent_cid=0
        List<CategoryEntity> collect = categoryEntities.stream().filter((item -> {
            return item.getParentCid().equals(0L);
        })).map((item) -> {
            item.setChildren(selectChildren(item, categoryEntities));
            return item;
        }).sorted(Comparator.comparingInt(c -> (c.getSort() == null ? 0 : c.getSort()))).collect(Collectors.toList());
        return collect;
    }

    @Override
    public void removeMenuByIds(List<Long> list) {

        //逻辑删除
        baseMapper.deleteBatchIds(list);
    }

    @Override
    @Transactional
    public void updateWithSort(CategoryEntity[] categorys) {
        List<CategoryEntity> list = Arrays.asList(categorys);
        for (CategoryEntity categoryEntity : list) {
            baseMapper.updateById(categoryEntity);
        }
    }

    /**
     * 更改信息必须更改其他冗余表
     * @param category
     */
    @Override
    public void updateDetail(CategoryEntity category) {
        // 先更改自己表
        this.updateById(category);
        // 更新关联表的冗余信息
        Long catId = category.getCatId();
        String categoryName = category.getName();
        categoryBrandRelationDao.updateCateInfo(catId,categoryName);
        // TODO: 2024/3/5 其他表待更新


    }



    @Override
    public List<CategoryEntity> getLevel1Categorys() {
        List<CategoryEntity> categoryEntities = this.baseMapper.selectList(
                new QueryWrapper<CategoryEntity>().eq("parent_cid", 0));
        return categoryEntities;
    }
    @Override
    public Map<String, List<Catelog2Vo>> getCatalogJson() {

        // TODO: 2024/3/18 参考B站评论封装为一个树方法  封装3级分类 待改进开始就不用Stream流
        /**
         *
         private String catalog1Id;
         private List<Catelog2Vo.Category3Vo> catalog3List;
         private String id;
         private String name;
         *
         */


        List<CategoryEntity> level1Categorys = this.getLevel1Categorys();
        return level1Categorys.stream().collect(Collectors.toMap(k -> k.getCatId().toString(), v -> {
            // 找到当前一级分类id下的所有二级分类 categoryEntities
            List<CategoryEntity> categoryEntities = baseMapper.selectList(new QueryWrapper<CategoryEntity>().
                    eq("parent_cid", v.getCatId()));
            // 遍历获取Catelog2Vo
            List<Catelog2Vo> list = new ArrayList<>();
            // 每个执行方法
            for (CategoryEntity categoryEntity : categoryEntities) {
                Catelog2Vo all3CatelgoryBy2Category = findAll3CatelgoryBy2Category(categoryEntity);
                list.add(all3CatelgoryBy2Category);
            }
            return list;
        }));

    }

    private Catelog2Vo findAll3CatelgoryBy2Category(CategoryEntity categoryEntity){
        // 传入为2级分类对象
        // 1） 找到所有的三级分类对象
        List<CategoryEntity> childens = baseMapper.selectList(new QueryWrapper<CategoryEntity>().
                eq("parent_cid", categoryEntity.getCatId()));
        // 2） 将三级分类对象变为Category3Vo
        List<Catelog2Vo.Category3Vo> collect = childens.stream().map(item -> {
            Catelog2Vo.Category3Vo category3Vo = new Catelog2Vo.Category3Vo();
            category3Vo.setCatalog2Id(item.getParentCid().toString());
            category3Vo.setId(item.getCatId().toString());
            category3Vo.setName(item.getName());
            return category3Vo;
        }).collect(Collectors.toList());
        // 3） 封装返回的Catelog2Vo、
        Catelog2Vo catelog2Vo = new Catelog2Vo();
        catelog2Vo.setCatalog3List(collect);
        catelog2Vo.setId(categoryEntity.getCatId().toString());
        catelog2Vo.setName(categoryEntity.getName());
        catelog2Vo.setCatalog1Id(categoryEntity.getParentCid().toString());

        return catelog2Vo;
    }

    private List<CategoryEntity> selectChildren(CategoryEntity root,List<CategoryEntity> all){
        List<CategoryEntity> collect = all.stream().filter((item) -> {
            return item.getParentCid().equals(root.getCatId());
        }).map((item) -> {
            item.setChildren((selectChildren(item, all)));
            return item;
        }).sorted(Comparator.comparingInt(c -> (c.getSort() == null ? 0 : c.getSort()))).collect(Collectors.toList());
        return collect;
    }

}